#include <sys/epoll.h>
#include <stdlib.h> //EXIT_SUCCESS
#include <stdio.h>
#include <string.h> //memset
#include <sys/types.h> //socket
#include <sys/socket.h>
#include <netinet/in.h> //ipv4
#include <netinet/ip.h>
#include <arpa/inet.h> //htonl
#include <signal.h>
#include <unistd.h> //fcntl
#include <fcntl.h>
#include <errno.h>

#define MAX_EVENTS 10

//将文件描述符设置为非阻塞模式
int setnonblocking(int fd)
{
    int flags = fcntl(fd, F_GETFL);
    if (flags < 0)
    {
        perror("fcntl");
        return flags;
    }
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

//处理客户端连接事件
void do_accept(int epollfd, int fd)
{
    struct sockaddr_in caddr;
    socklen_t addrlen = sizeof(caddr);
    int connfd = accept(fd, (struct sockaddr *)&caddr, &addrlen);
    if (connfd < 0 && (errno != EAGAIN || errno != EWOULDBLOCK))
    {
        perror("accept");
        return;
    }

    //打印客户端IP地址
    char ipstr[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &caddr.sin_addr, ipstr, sizeof ipstr);
    printf("client %s connected\n", ipstr);

    setnonblocking(connfd);

    //增加连接socket
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = connfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev))
    {
        perror("EPOLL_CTL_ADD");
        close(connfd);
        return;
    }
}

void do_disconnect(int epollfd, int fd)
{
    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL) < 0)
    {
        perror("EPOLL_CTL_DEL");
    }

    //获取并打印客户端IP地址
    struct sockaddr_in caddr;
    socklen_t addrlen = sizeof(caddr);
    if (getpeername(fd, (struct sockaddr*)&caddr, &addrlen) < 0)
    {
        perror("getpeername");
    }

    char ipstr[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &caddr.sin_addr, ipstr, sizeof ipstr);
    printf("client %s disconnected\n", ipstr);

    close(fd);
}

//处理客户端数据
void do_read(int epollfd, int fd)
{
    //打印接收到的数据，当客户端断开或出现EAGAIN或EWOULDBLOCK错误时结束
    char c;
    ssize_t n;
    while ((n = read(fd, &c, 1)) == 1)
    {
        write(1, &c, 1);
    }

    if (n < 0 && (errno != EAGAIN || errno != EWOULDBLOCK))
    {
        perror("read");
    }
    //客户端断开连接
    if (n == 0)
    {
        do_disconnect(epollfd, fd);
    }
}

int server_init(short port)
{
    //忽略SIGPIPE信号
    signal(SIGPIPE, SIG_IGN);

    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket");
        return -1;
    }

    if (setnonblocking(listenfd) < 0)
    {
        perror("setnonblocking");
    }

    //监听地址重用
    int opt = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
    {
        perror("setsockopt");
    }

    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(port);
    if (bind(listenfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind");
        return -1;
    }

    if (listen(listenfd, MAX_EVENTS) < 0)
    {
        perror("listen");
        return -1;
    }

    return listenfd;
}

int main()
{
    int listenfd = server_init(80);
    if (listenfd < 0)
    {
        return EXIT_FAILURE;
    }

    int epollfd = epoll_create1(0);
    if (epollfd < 0)
    {
        perror("epoll_create1");
        return EXIT_FAILURE;
    }

    //增加监听套接字
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev))
    {
        perror("EPOLL_CTL_ADD");
        return EXIT_FAILURE;
    }

    struct epoll_event* events = calloc(MAX_EVENTS, sizeof(struct epoll_event));
    if (!events)
    {
        perror("calloc");
        return EXIT_FAILURE;
    }

    puts("server start");

    for (;;)
    {
        //等待事件
        int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds < 0)
        {
            perror("epoll_wait");
        }

        //处理事件
        for (int i = 0; i < nfds; i++)
        {
            if (events[i].data.fd == listenfd)
            {
                do_accept(epollfd, listenfd);
            }
            else
            {
                do_read(epollfd, events[i].data.fd);
            }
        }
    }

    //不会执行到这里
    free(events);

    return EXIT_SUCCESS;
}